package cn.melonlib.cores.jpa.comp.dao.bean.collections;

import cn.melonlib.cores.jpa.model.bean.Query;
import cn.melonlib.cores.jpa.model.bean.collections.JsonMap;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;

import java.io.IOException;
import java.net.URLEncoder;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

public class HqlConditionMap<V> extends JsonMap<V> {
    public static final Pattern CDT_REGEX=Pattern.compile("((and|or)\\^)?((\\w+)(\\.\\w+)*)(\\$(.+))?");
    public HqlConditionMap() {
        super();
    }

    public HqlConditionMap(int initialCapacity, float loadFactor,
                           boolean accessOrder) {
        super(initialCapacity, loadFactor, accessOrder);
    }

    public HqlConditionMap(int initialCapacity, float loadFactor) {
        super(initialCapacity, loadFactor);
    }

    public HqlConditionMap(int initialCapacity) {
        super(initialCapacity);
    }

    public HqlConditionMap(Map<? extends String, ? extends V> m) {
        super(m);
    }

    public HqlConditionMap(String json) throws JsonParseException, JsonMappingException, IOException {
        super(json);
    }


    public V put(String key, V value) {
        return super.put(key, value);
    }

    public HqlConditionMap<V> appendAll(Map<String,V> map){
        if(map!=null) {
            super.putAll(map);
        }
        return this;
    }


    @Override
    public HqlConditionMap<V> append(String key, V value){
        if(value==null){
            return this;
        }
        super.append(key, value);
        return this;
    }
    public HqlConditionMap<V> addPrm(String key, V value) {
        if(value==null) {
            return this;
        }
        if(((value instanceof CharSequence)&&(StringUtils.isBlank((CharSequence) value)))||"null".equals(value)){
            return this;
        }
        if(key.contains("like")){
            return this.append(key, (V) ("%"+value+"%"));
        }
        if(key.endsWith(":string")) {
            return this.append(key.substring(0, key.indexOf(":string")), (V) value.toString());
        }
        if(key.endsWith(":int")) {
            return this.append(key.substring(0, key.indexOf(":int")), (V) NumberUtils.createInteger(value.toString()));
        }
        if(key.endsWith(":long")) {
            return this.append(key.substring(0, key.indexOf(":long")), (V) NumberUtils.createLong(value.toString()));
        }
        if(key.endsWith(":double")) {
            return this.append(key.substring(0, key.indexOf(":double")), (V) NumberUtils.createDouble(value.toString()));
        }
        if(key.endsWith(":boolean")) {
            return this.append(key.substring(0, key.indexOf(":boolean")), (V) new Boolean(BooleanUtils.toBoolean(value.toString())));
        }
        return this.append(key, value);
    }
    public String getContent(){
        Set<String> names=keySet();
        StringBuilder hql=new StringBuilder();
        int i=0;
        for (String name : names) {
            Matcher m=CDT_REGEX.matcher(name);
            if(m.matches()){
                StringBuilder cdthql=new StringBuilder();
                String prefix=m.group(2);
                String column=m.group(3);
                String suffix=m.group(7);
                if(StringUtils.isBlank(prefix)){
                    prefix="and ";
                }
                if(StringUtils.isBlank(suffix)){
                    suffix="=";
                }

                if(i==0){
                    cdthql.append(" ");
                }else{
                    cdthql.append(prefix).append(" ");
                }
                Object value=get(name);
                if(value instanceof List){
                    List col=(List) value;
                    if(col.size()>0){
                        int j=0;
                        cdthql.append(column).append(suffix.equals("not")?" not":"").append(" in(");
                        for (Object object : col) {
                            if(j!=0){
                                cdthql.append(",");
                            }
                            cdthql.append("?"+(j+1));
                            j++;
                        }
                        cdthql.append(") ");
                    }else{
                        continue;
                    }
                }else if(value instanceof Map){
                    cdthql.append("(");
                    HqlConditionMap<Object> inner=new HqlConditionMap<>((Map)value);
                    cdthql.append(inner.getContent());
                    cdthql.append(")");
                }else if(value instanceof Query) {
                    switch (suffix) {
                        case "ex":{
                            cdthql.append(" exists ");
                            break;
                        }
                        case "nex":{
                            cdthql.append(" not exists ");
                            break;
                        }
                        case "in":{
                            cdthql.append(" in ");
                            break;
                        }
                        case "nin":{
                            cdthql.append(" not in ");
                            break;
                        }

                    }
                    cdthql.append("(");
                    cdthql.append(((Query) value).getContent());
                    cdthql.append(")");
                } else if("null".equals(suffix)){
                    cdthql.append(column).append(" is null ");
                }else if("exist".equals(suffix)){
                    cdthql.append(column).append(" is not null ");
                }else{
                    cdthql.append(column).append(" ").append(suffix).append(" ? ");
                    if((value instanceof CharSequence)&&(StringUtils.isBlank((CharSequence) value)||"null".equals(value))){
                        continue;
                    }
                }
                hql.append(cdthql);
                i++;
            }
        }
        return hql.toString();
    }

    public List<V> getParams(){
        List<V> params=new ArrayList<>();
        for (String key: keySet()) {
            if(key.endsWith("$null")){
                continue;
            }
            if(key.endsWith("$exist")){
                continue;
            }
            V val=get(key);
            if(val instanceof Query) {
                params.addAll((Collection<? extends V>) ((Query) val).getParams());
                continue;
            }
            if(val instanceof List){
                params.addAll((List<? extends V>) val);
                continue;
            }
            if(val instanceof Map){
                HqlConditionMap<V> inner=new HqlConditionMap<>((Map)get(key));
                params.addAll(inner.getParams());
                continue;
            }
            params.add(val);
        }
        return params;
    }

    public String toUrl() throws Exception{
        return this.keySet().stream().sorted().filter(key-> ObjectUtils.anyNotNull(get(key)))
                .filter(key->StringUtils.isNotBlank(get(key).toString())).map(key->{
                    StringBuilder item=new StringBuilder();
                    Object val=get(key);
                    item.append(key).append("=");
                    if(val instanceof Collection) {
                        item.append(((Collection) val).stream().collect(Collectors.joining(",")));
                    }else {
                        item.append(val.toString());
                    }
                    return item;
                }).collect(Collectors.joining("&"));
    }

    public String toSearchParams()throws Exception{
        Set<String> names=keySet();
        StringBuilder pstr=new StringBuilder();
        int i=0;
        for (String name : names) {
            Matcher m=CDT_REGEX.matcher(name);
            if(m.matches()){
                StringBuilder cdthql=new StringBuilder();
                String prefix=m.group(2);
                String column=m.group(3);
                String suffix=m.group(7);
                if(StringUtils.isBlank(prefix)){
                    prefix=" ";
                }
                if(StringUtils.isBlank(suffix)){
                    suffix=":";
                }

                if(i==0){
                    cdthql.append(" ");
                }else{
                    cdthql.append(prefix).append(" ");
                }
                Object value=get(name);
                if(value instanceof Collection){
                    Collection col=(Collection) value;
                    if(col.size()>0){
                        int j=0;
                        cdthql.append(column).append(":(");
                        for (Object object : col) {
                            if(j!=0){
                                cdthql.append(" ");
                            }
                            cdthql.append(object);
                            j++;
                        }
                        cdthql.append(") ");
                    }else{
                        continue;
                    }
                }else{
                    switch (suffix) {
                        case ">":
                            cdthql.append(column).append(":[ ").append(value).append(" TO *]");
                            break;
                        case "<":
                            cdthql.append(column).append(":[ * TO ").append(value).append(" ]");
                            break;
                        default:
                            cdthql.append(column).append(":").append(value);
                    }
                }
                pstr.append(cdthql);
                i++;
            }
        }
        return URLEncoder.encode(pstr.toString().replaceAll("\\s+"," ").trim(), "UTF-8");
    }

    public Properties transProps(){
        Properties properties=new Properties();
        for (String key : keySet()) {
            properties.setProperty(key, String.valueOf(get(key)));
        }
        return properties;
    }

    public String toUpdate() {
        return this.keySet().stream().map(key->new StringBuilder(key).append("=?")).collect(Collectors.joining(","));
    }

    public String toSimpleString(){
        StringBuilder res=new StringBuilder();
        int i=0;
        for (String buff : keySet()) {
            if(i!=0){
                res.append(",");
            }
            res.append(buff).append("=").append(get(buff));
            i++;
        }
        return res.toString();
    }
}
